package student.productions;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;


public class Term extends MutableNode {
	// Invariant for children: children has at least one Factor.
	// children alternates Factor and AddOp, starting with Factor and having an odd number of elements.
	
	/**
	 * Constructor: creates an instance of Term whose children are all the Nodes in allChildren.
	 * @param allChildren list of children.
	 * Requires: Contents of allChildren are in form: Factor ( AddOp Factor )*
	 */
	public Term(List<AbstractNode> allChildren) {
		if(allChildren.size() > 0)
			children= allChildren;
		else
			throw new IllegalArgumentException("There must be at least one child to make this production.");
	}

	@Override
	public void prettyPrint(StringBuffer sb) {
		for(Node n : children)
			n.prettyPrint(sb);
	}

	@Override
	public void mutate(Program genome) {
		StringBuffer sb = new StringBuffer();
		prettyPrint(sb);
		sb.append("was changed to ");
		Random rnd = new Random();
		int mutationType;
		if (children.size() == 1) {
			mutationType = rnd.nextInt(1);
		} else {
			mutationType = rnd.nextInt(3);
		}
		if (mutationType == 0) { // Mutates by replacing a Factor with a randomly chosen or randomly generated Factor.
			int randomIndex = rnd.nextInt(children.size()/2 + 1);
			int evenIndex = randomIndex*2; // This chooses a random even index so that a Factor is chosen.
			Factor newFactor = AbstractNode.chooseFactor(genome, children.get(randomIndex));
			children.set(evenIndex, newFactor);
		} else if (mutationType == 1) { // Mutates by reversing order of children.
			Collections.reverse(children);
		} else { // Mutates by replacing this Term's children with one of its children.
			int randomIndex = rnd.nextInt(children.size()/2 + 1);
			int evenIndex = randomIndex*2; // This chooses a random even index so that an Factor is chosen.
			AbstractNode replace = children.get(evenIndex);
			children.clear();
			children.add(replace);
		}
		prettyPrint(sb);
		System.out.println(sb);
	}

	@Override
	public Node duplicate() {
		List<AbstractNode> duplicateChildren= new LinkedList<AbstractNode>();
		for (AbstractNode n : children) {
			duplicateChildren.add((AbstractNode)n.duplicate());
		}
		return new Term(duplicateChildren);
	}

}
